#ifndef _TESTBED_GL_
#define _TESTBED_GL_
#include<vector>
#include<list>
//#include "config.h"
#include<fstream>
#include<string>
#include<GL/glew.h>
#include<GL/gl.h>
#include<SDL.h>
#include<string>
#include<iostream>
#include<boost/shared_array.hpp>
#include<boost/shared_ptr.hpp>
#include <stdlib.h>
#include<math.h>
#define FIND_AND_SKIP_3DS_CHUNK(a) do { header head = GetHeader(); a else stream->seekg(head.second-6,std::ios_base::cur); }while(!stream->eof());
#define IMAGE_FORMAT(str,format_,in_format_,type_)  if (in_format.compare(str)==0) {format = format_; internal_format = in_format_;}
#define VECTOR_TO_ARRAY(array,vector)
#define _VECTORMATH_DEBUG


#if defined(__SSE__)||defined(__MMX__)
#include "cpp/vectormath_aos.h"
#else
#include "cpp/vectormath_aos.h"
#endif
#include <memory.h>
#include <hash_map>
using namespace Vectormath::Aos;
#define MAX(a,b) a>b?a:b
#define MIN(a,b) a<b?a:b

//We going to use handmade TGA loader to avoid external dependency
//In some cases we will use
#define HANDMADE

/** \brief storage class for 2d images
 *
 */


struct Image
{
private:
public:
    boost::shared_ptr<char> data;
    int sizex,sizey,bpp;
    std::string format;
    Image()
    {
        sizex = 0;
        sizey= 0;
        bpp =0;
        format = "";
    }
    virtual ~Image()
    {
    }
};

#ifdef __GNUC__
#include<ext/hash_map>
namespace __gnu_cxx
{
template<> struct hash<std::string>
{
    size_t operator()(std::string __s) const
    {
        return __stl_hash_string(__s.c_str());
    }
};
}
using namespace __gnu_cxx;
#else
#include<hash_map>
#endif
using namespace std;

/** \file testbedGL.hpp
 * \brief simple wrapper for common graphics functions.
 *
 * Supports simple scene graph, VBO, vertex and pixel shaders, render targets.
 * We do use structures instead of classes in most cases. It is done because we don't need member data encapsulation to keep
 * data handling as explicit as possbile.
 */

namespace TestbedGL
{
#ifdef FREEIMAGE
#endif
#ifdef DEVIL
#endif
#ifdef HANDMADE


/** \brief loads TGA image file into Image structure.
 *
 * \ Supports both 24 bits and 32 bits with alpha channel. Also supports RLE conversion
 * \param name name of image file
 * \param image reference
 * \return image data through reference
 */
static void load(std::string name, Image &image)
{
    unsigned char image_header[12];
    unsigned char header[6];
    try
    {
        std::ifstream file(name.c_str(), std::ios::binary | std::ios::in);
        if(file.fail())
        {
            throw "File is bad\n";
        }
        char compressed[12] = {0,0,10, 0,0,0, 0,0,0 ,0,0,0};
        char uncompressed[12] = {0,0,2, 0,0,0, 0,0,0 ,0,0,0};
        file.read((char*)image_header,12);
        file.read((char*)header,6);
        image.sizex = abs(header[1] * 256 + header[0]);
        image.sizey = abs(header[3] * 256 + header[2]);
        image.bpp =  header[4];
        if (image.bpp==24) image.format = "GL_BGR_EXT";
        else if (image.bpp == 32) image.format = "GL_BGRA_EXT";
        else printf ("Incorrect format\n");
        int size = image.sizex * image.sizey * image.bpp/8;
        int pixelcount = image.sizex * image.sizey;
        image.data = boost::shared_ptr<char>(new char[size]);
        int bytes = image.bpp/8;
        if (memcmp(image_header,compressed,12)== 0)   //compressed
        {
            unsigned int currentbyte = 0;
            int current_pixel = 0;
            unsigned char chunkheader = 0;
            do
            {
                file.read((char*)&chunkheader,1);

                if (chunkheader < 128)
                {
                    char color[4];
                    chunkheader++;

                    for (unsigned int i=0; i < chunkheader; i++)
                    {
                        file.read(color,bytes);
                        memcpy((void*)&(image.data.get()[currentbyte+i*bytes]),(void*)color,bytes);
                    }
                    currentbyte+=chunkheader*bytes;
                    current_pixel+=chunkheader;
                }
                else
                {
                    chunkheader -= 127;
                    char *color = new char[4];
                    file.read(color,bytes);
                    for (unsigned int i = 0; i < chunkheader; i++)
                        memcpy((void*)&(image.data.get()[currentbyte+i*bytes]),(void*)color,bytes);

                    currentbyte+=chunkheader*bytes;
                    current_pixel+=chunkheader;
                }
            }
            while (current_pixel < pixelcount);  //Line 63
        }
        if (memcmp(image_header,uncompressed,12)== 0)
        {
            file.read(image.data.get(), size);/*Simple BGR/BGRA format */
        }
        file.close();
    }
    catch (...)
    {
        printf("Error opening file %s\n",name.c_str());
    }
}
#endif

/** \brief converts euler angles into quaternion
 *
 * A more extensive description of myProcedure().
 * \param heading heading value.
 * \param altitude altitude value
 * \param bank bank value
 * \return Quat structure.
 */
static Quat euler_to_quat(float heading,float attitude,float bank)
{
    float c1,c2,c3,s1,s2,s3;
    float x,y,z,w;
    c1 = cos(heading / 2.0f);
    c2 = cos(attitude / 2.0f);
    c3 = cos(bank / 2.0f);
    s1 = sin(heading / 2.0f);
    s2 = sin(attitude / 2.0f);
    s3 = sin(bank / 2.0f);
    w = c1 * c2 * c3 - s1 * s2 * s3;
    x = s1 * s2 * c3 + c1 * c2 * s3;
    y = s1 * c2 * c3 + c1 * s2 * s3;
    z = c1 * s2 * c3 - s1 * c2 * s3;
    return  Quat(w,z,y,x);
}

/** \brief Matrix4GL is a wrapper from math library to OpenGL matrices
 *
 *  This class implements several useful extensions to Matrix4 class from math library
 */
class Matrix4GL:public Matrix4
{
public:
    Matrix4GL()
    {
    }

    Vector3 get_dir()
    {
        return Vector3(getElem(0,2),getElem(1,2),getElem(2,2));
    }

/** \brief returns float[16] matrix for OpenGL
 *
 * Updates and returns the OpenGL matrix
 * \return float[16] array.
 */
    float* get_matrix()
    {
        static float matrix[16];
        matrix[0] = getCol0()[0];
        matrix[1] = getCol0()[1];
        matrix[2] = getCol0()[2];
        matrix[3] = getCol0()[3];
        matrix[4] = getCol1()[0];
        matrix[5] = getCol1()[1];
        matrix[6] = getCol1()[2];
        matrix[7] = getCol1()[3];
        matrix[8] = getCol2()[0];
        matrix[9] = getCol2()[1];
        matrix[10] = getCol2()[2];
        matrix[11] = getCol2()[3];
        matrix[12] = getCol3()[0];
        matrix[13] = getCol3()[1];
        matrix[14] = getCol3()[2];
        matrix[15] = getCol3()[3];
        return matrix;
    }

/** \brief sets matrix value in linear matrix style
 *
 * \param index contains linear index
 * \param contains value
 */
    void set(int index,float value)
    {
        int col = index / 4;
        int row = index % 4;
        setElem(col,row,value);
    }

/** \brief sets matrix translation
 *
 * It is done not through multiplication because sometimes we need to preserve orientation.
 * \param index contains linear index
 * \param x x-axis postion
 * \param y y-axis postion
 * \param z z-axis postion
 */
    void set_translation(float x,float y,float z)
    {
        set(12,x);
        set(13,y);
        set(14,z);
    }
/** \brief copies from OpenGL matrix into 3d-math matrix
 *
 * \param  values pointer to the first element of OpenGL matrix
 */
    void set_matrix(float *values)
    {
        for(int i = 0; i < 16; i++)
            set(i,values[i]);
    }
    void  operator = (const Quat& quat_)
    {
        Quat quat;
        quat = normalize(quat_);
        float wx, wy, wz, xx, yy, yz, xy, xz, zz, x2, y2, z2;
        x2 = quat.getX() +  quat.getX();
        y2 =  quat.getY() +  quat.getY();
        z2 =  quat.getZ() +  quat.getZ();
        xx =  quat.getX() * x2;
        xy = quat.getX() * y2;
        xz =  quat.getX() * z2;
        yy =  quat.getY() * y2;
        yz = quat.getY() * z2;
        zz =  quat.getZ() * z2;
        wx =  quat.getW() * x2;
        wy = quat.getW() * y2;
        wz =  quat.getW() * z2;
        set(0,1.0f-(yy+zz));
        set(1,xy-wz);
        set(2,xz+wy);
        set(4,xy+wz);
        set(5,1.0f-(xx+zz));
        set(6,yz-wx);
        set(8,xz-wy);
        set(9,yz+wx);
        set(10,1.0f-(xx+yy));
        set(3,0);
        set(7,0);
        set(11,0);
        set(12,0);
        set(13,0);
        set(14,0);
        set(15,1.0f);

    }

/** \brief copy constructor
 */
    Matrix4GL(const Matrix4& mat)
    {
        setCol0(mat.getCol0());
        setCol1(mat.getCol1());
        setCol2(mat.getCol2());
        setCol3(mat.getCol3());
    }
};

/** \brief float values storage class.
 */
struct FloatArray
{
    /** \brief width element count in one vertex
     */
    unsigned int width;

    /** \brief size total element count.
     */
    unsigned int size;

    /** \brief stride stride between elements in interleaved elements
     */
    unsigned  int stride;

    /** \brief array value of array
     */
    boost::shared_array<float>array;

    /** \brief name
     */
    std::string name;

    /** \brief start stride for first element. Used in interleaved arrays
     */
    unsigned int start;

    /** \brief width element count in one vertex
     */
    GLint id;

    /** \brief width element count in one vertex
     */
    std::vector<float>tmp_array;

    /** \brief parametered constructor
     * \param  width count of values per element
     * \param  size total count of elementsarray
     * \param  stride stride between elements
     * \param  array pointer to the array
     * \param  name name for array
     */
    FloatArray(int width_,int size_,int stride_,boost::shared_array<float>array_,std::string name_):width(width_),size(size_),stride(stride_),array(array_),name(name_)
    {
        start = 0;
        tmp_array.clear();
    }
    /** \brief simple consturctor
    */
    FloatArray()
	{
	    start = 0;
        tmp_array.clear();
	}

    /** \brief returns pointer to the first element in array
     */
    float* get()
    {
        return (float*)(array.get()+start);
    }

    /** \brief repack function repacking elements into interleave array
     */
    void repack()
    {
        boost::shared_array<float>tmp(new float[size*width]);
        unsigned int diff = (size)*width - tmp_array.size();
        for(unsigned int i = 0; i < diff; i++)
            tmp[i] = array[i];
        for(unsigned int i = 0; i < tmp_array.size(); i++)
            tmp[i+diff] = tmp_array[i];
        tmp_array.clear();
        array = tmp;
    }

};

/** \brief storage class for integer indecies
    */
struct  FaceArray
{
public:
    /** \brief size total count of indecies in array
    */
    int size;
    boost::shared_array<unsigned int>array;
    /** \brief parametered constructor
     * \param  size total count of elements in array
     * \param pointer to the array
     */
    FaceArray(int size_,boost::shared_array<unsigned int>array_):size(size_),array(array_) {}
    unsigned int& operator [] (int id)
    {
        return array[id];
    }
    FaceArray() {}
};

struct GeometryLink
{
    /** \brief index of vertex array in arrays
    */
    int vertex_array;
    /** \brief index of normal array in arrays
    */
    int normal_array;
    /** \brief indecies of texture coordinate array in arrays
    */
    std::vector<int>tex_coords;
    /** \brief attributes of texture coordinate array in arrays
    */
    std::vector<int>attributes;
    /** \brief pointers to float arrays
    */
    std::vector<FloatArray>arrays;
    /** \brief simple constructor
    */
    GeometryLink():normal_array(-1)
    {
    }
    /** \brief function for adding the vertex array into GeometryLink
    *  \param va link to instance of float Array
    */
    void set_vertex_array(const FloatArray& va)
    {
        arrays.push_back(va);
        vertex_array = arrays.size()-1;
    }
    /** \brief function for adding the normal array into GeometryLink
    *  \param na link to instance of float Array
    */
    void set_normal_array(const FloatArray& na)
    {
        arrays.push_back(na);
        normal_array = arrays.size()-1;
    }
    /** \brief function for adding the texture coord array into GeometryLink. You can have multiple arrays of texture coords per GeometryLink.
    *  \param ta link to instance of float Array
    */
    void add_tex_coord_array(const FloatArray& ta)
    {
        arrays.push_back(ta);
        tex_coords.push_back(arrays.size()-1);
    }
    /** \brief function for adding the attributes array into GeometryLink.
    *  \param aa link to instance of float Array
    *  \param idx index in shader for this attribute.
    */
    void add_attribute_array(const FloatArray &aa,GLint idx)
    {
        arrays.push_back(aa);
        arrays.rbegin()->id = idx;
        attributes.push_back(arrays.size()-1);
    }
    /** \brief binds arrays to current state. NB: It doesn't turn on or off any of the states. So you should do it manually.
    */
    void bind()
    {
        glVertexPointer(arrays[vertex_array].width,GL_FLOAT,arrays[vertex_array].stride*sizeof(float),arrays[vertex_array].get());
        glNormalPointer(GL_FLOAT,arrays[normal_array].stride*sizeof(float),arrays[normal_array].get());
        for (unsigned int i = 0; i < tex_coords.size(); i++)
        {
            glTexCoordPointer( arrays[tex_coords[i]].width, GL_FLOAT, arrays[tex_coords[i]].stride*sizeof(float), arrays[tex_coords[i]].get());
        }

	}
};

/** \brief structure representing the Geometry objects in scene.
    */

struct Geometry
{
public:
    /** \brief name is identificator
    */
    std::string name;
    /** \brief identificator for VBO.
    */
    GLuint vertex_handler;
    /** \brief identificator for VBO.
    */
    GLuint index_handler;
    /** \brief pointer to the array. Used when data of GeometryLink is packed into interleaved array
    */
    boost::shared_array<float>vertex_data;
    /** \brief total count of float values in vertex
    */
    unsigned int vertex_size;
    /** \brief instance of FaceArray
    */
    FaceArray faces;
    /** \brief instance of GeometryLink.
    */
    GeometryLink link;
    /** \brief tryes to bind the VBO and GeometryLink
    */
	void bind()
    {
        glBindBuffer(GL_ARRAY_BUFFER,vertex_handler);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB,index_handler);
        link.bind();
    }
    /** \brief repack arrays in GeometryLink into vertex_data. The pointer in GeometryLink will be changed to point to the according address in vertex_data. You don't need to use this functions if initVBO is used.
    */
    void pack_arrays()
    {
        unsigned int size = 0;
        for (unsigned int i = 0; i < link.arrays.size(); i++)
        {
            size += link.arrays[i].width;
        }

        vertex_size = size;
        vertex_data = boost::shared_array<float> (new float[ link.arrays.begin()->size * size ]);

        for (unsigned int i = 0; i < link.arrays.begin()->size; i++)
        {
            int stride = 0;
            for (unsigned int j = 0; j < link.arrays.size(); j++)
            {
                for (unsigned int k = 0; k < link.arrays[j].width; k++)
                {
                    vertex_data[i*size+stride+k] = link.arrays[j].array[i*link.arrays[j].width+k];
                }
                stride += link.arrays[j].width;
            }
        }
        unsigned int start = 0;
        for(std::vector<FloatArray>::iterator it = link.arrays.begin(); it != link.arrays.end(); it++)
        {
            it->array.reset();
            it->stride = vertex_size;
            it->start = start;
            start += it->width;
        }

    }

    /** \brief repacks arrays and puts them into videocard memory
    */
    void initVBO()
    {
        pack_arrays();

        glGenBuffers(1,&vertex_handler);
        glBindBuffer(GL_ARRAY_BUFFER,vertex_handler);
        glBufferData(GL_ARRAY_BUFFER, link.arrays[0].size * vertex_size * sizeof(float), vertex_data.get(), GL_STATIC_DRAW);
        unsigned int start = 0;
        for(std::vector<FloatArray>::iterator it = link.arrays.begin(); it != link.arrays.end(); it++)
        {
            it->array.reset();
            it->stride = vertex_size;
            it->start = start;
            start += it->width;
        }

        glGenBuffers(1,&index_handler);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER_ARB, index_handler);
        glBufferData(GL_ELEMENT_ARRAY_BUFFER_ARB, faces.size * sizeof(unsigned int), faces.array.get(), GL_STATIC_DRAW);
        faces.array.reset();

        glBindBuffer(GL_ARRAY_BUFFER,0);
        glBindBuffer(GL_ELEMENT_ARRAY_BUFFER,0);
    }
    /** \brief simple constructor
    */
    Geometry()
    {
        vertex_handler = 0;
        index_handler = 0;

    }
    /** \brief simple destructor
    */
    ~Geometry()
    {
        vertex_data.reset();

        if(vertex_handler)
            glDeleteBuffers(1,&vertex_handler);

        if(index_handler)
            glDeleteBuffers(1,&index_handler);

    }
};

/** \brief Scene graph class. Contains both links to the geometry and children nodes
*/
class SceneNode
{
public:
    /**
    */
    std::string material;

    /** \brief identificator for node
    */
    std::string name;
    /** \brief matrix representing initial transformation of Node
    */
    Matrix4GL base_matrix;
    /** \brief matrix with all tranformation need for rendering
    */
    Matrix4GL matrix;
    /** \brief geometry pointers array.
    */
	std::vector<boost::shared_ptr<Geometry> >geometry_ids;
    /** \brief children of the node
    */
	std::vector<boost::shared_ptr<SceneNode> >children;
    /** \brief dictionary for quick access by name. build_dict() should be used before accessing values.
    */
	hash_map<std::string,boost::shared_ptr<SceneNode> >dict;
    /** \brief simple constructor
    */
    SceneNode()
    {
        base_matrix = Matrix4::identity();
        matrix = Matrix4::identity();
    }
    /** \brief builds hash_map of children nodes. The access key is name of child node.
    */
    void build_dict()
    {
        for(std::vector<boost::shared_ptr<SceneNode> >::iterator it = children.begin(); it != children.end(); it++)
        {
            dict[(*it)->name] = *it;
        }
    }
    /** \brief copy constructor
    */
    SceneNode(const SceneNode& node)
    {
        name = node.name;
        base_matrix = node.base_matrix;
        matrix = node.matrix;
        geometry_ids.resize(node.geometry_ids.size());
        std::copy(node.geometry_ids.begin(),node.geometry_ids.end(),geometry_ids.begin());
		for( std::vector<boost::shared_ptr<SceneNode> >::const_iterator node_it = node.children.begin(); node_it != node.children.end(); node_it++)
        {
            SceneNode* node = node_it->get();
            children.push_back(boost::shared_ptr<SceneNode>(new SceneNode(*node)));
        }
    }

    /** \brief accessing children array style.
    */
	boost::shared_ptr<SceneNode> operator[](std::string name)
	{
	    return dict[name];
	}
    /** \brief updates children matrices
    */
    void update()
    {
        for (unsigned int i = 0; i < children.size(); i++) children[i]->matrix = children[i]->base_matrix * base_matrix;
    }
};


/** \brief this class is not implemented so documentation will be written when class is ready.
*/
class StateObject
{
public:
    StateObject(int att_count,int textures)
    {
        tex_coord_buffer = textures;
        attributes.resize(att_count);
    }
    unsigned int vertex_buffer,tex_coord_buffer,normal_buffer;
    unsigned int blend_mode,cullface_mode,fbo;
    std::vector< GLuint >enable_states;
    std::vector< GLuint >attributes;
    void add_enable_flag(int flag)
    {
        enable_states.push_back(flag);
    }
    void bind()
    {
        for (std::vector<GLuint>::iterator iter = enable_states.begin(); iter < enable_states.end(); iter++) glEnable(*iter);
        if (vertex_buffer)
        {
            glEnableClientState(GL_VERTEX_ARRAY);
        }
        if (normal_buffer)
        {
            glEnableClientState(GL_NORMAL_ARRAY);
        }
        for (unsigned int i = 0; i < tex_coord_buffer; i++)
        {
            glClientActiveTextureARB ( GL_TEXTURE0_ARB+i);
            glEnableClientState ( GL_TEXTURE_COORD_ARRAY );
        }
        glClientActiveTextureARB(GL_TEXTURE0);
        for (std::vector<GLuint>::iterator iter = attributes.begin(); iter < attributes.end(); iter++) glEnableVertexAttribArray(*iter);
    }
    void unbind()
    {
        for (std::vector<GLuint>::iterator iter = enable_states.begin(); iter < enable_states.end(); iter++) glDisable(*iter);
        if (vertex_buffer)
        {
            glDisableClientState(GL_VERTEX_ARRAY);
        }
        if (normal_buffer)
        {
            glDisableClientState(GL_NORMAL_ARRAY);
        }
        for (unsigned int i = tex_coord_buffer; i > 0; --i)
        {
            glClientActiveTextureARB ( GL_TEXTURE0_ARB+i);
            glDisableClientState ( GL_TEXTURE_COORD_ARRAY );
        }
        for (std::vector<GLuint>::iterator iter = attributes.begin(); iter < attributes.end(); iter++) glDisableVertexAttribArray(*iter);
    }
    void set_blend_mode() {}
};


/** \brief texture class supporting RGB,RGBA and Depth types
*/
struct Texture
{
private:
public:
    /** \brief OpenGL hanlder for texture
    */
    GLuint  handler;
    /** \brief string texture format descripting
    */
    std::string format;
    /** \brief loads TGA image(only supported by now) and build a textur out of it.
    */
    void load_image(std::string path)
    {
        Image image;
        load(path,image);
        build(image.format,image.sizex,image.sizey,image.data.get());
    }
    /** \brief texture building function
    * \param format type
    * \param width
    * \param height
    * \param data pointer. NULL if it is provided by RenderTarget
    * \param mipmapped not used by now.
    */
    void build(std::string format_,int sizex,int sizey, char *data, bool mipmapped = false)
    {
        int format,internal_format;
        std::string in_format = format_;

        IMAGE_FORMAT("GL_BGR_EXT",GL_BGR_EXT,GL_RGB,GL_UNSIGNED_BYTE);
        IMAGE_FORMAT("GL_BGRA_EXT",GL_BGRA_EXT,GL_RGBA,GL_UNSIGNED_BYTE);
        IMAGE_FORMAT("GL_RGB",GL_RGB,GL_RGB8,GL_UNSIGNED_BYTE);
        IMAGE_FORMAT("GL_RGBA",GL_RGBA,GL_RGBA8,GL_UNSIGNED_BYTE);
        IMAGE_FORMAT("GL_DEPTH_COMPONENT",GL_DEPTH_COMPONENT,GL_DEPTH_COMPONENT,GL_FLOAT);

        glEnable(GL_TEXTURE_2D);
        glGenTextures(1,&handler);
        glBindTexture(GL_TEXTURE_2D, handler);

        if(format_.compare("GL_DEPTH_COMPONENT")==0)
        {

            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_MODE, GL_COMPARE_R_TO_TEXTURE);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_COMPARE_FUNC, GL_LESS | GL_EQUAL);
            glTexParameteri(GL_TEXTURE_2D, GL_DEPTH_TEXTURE_MODE, GL_INTENSITY);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST);
            glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST);

             glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP );
             glTexParameterf( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP );
             //glTexParam    eteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_FALSE); // automatic mipmap
             glTexImage2D(GL_TEXTURE_2D, 0,internal_format, sizex, sizey, 0,format, GL_FLOAT, data);
             glBindTexture(GL_TEXTURE_2D, 0);
        }
        else
        {
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT );
            glTexParameteri( GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT );
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
            glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR_MIPMAP_LINEAR);
            glTexParameteri(GL_TEXTURE_2D, GL_GENERATE_MIPMAP, GL_TRUE); // automatic mipmap
            glTexImage2D(GL_TEXTURE_2D, 0,internal_format, sizex, sizey, 0,format, GL_UNSIGNED_BYTE, data);
            glBindTexture(GL_TEXTURE_2D, 0);

        }
        glTexImage2D(GL_TEXTURE_2D, 0,internal_format, sizex, sizey, 0,format, GL_UNSIGNED_BYTE, data);
        glBindTexture(GL_TEXTURE_2D, 0);
    }
     /** \brief destructor only thing we need to destroy is handler.
    */
    virtual ~Texture()
    {
        glDeleteTextures(1,&handler);
    }
};

 /** \brief wrapper for easy access of shader uniform values. It is up to user to manage the type. There is no verification if you are passing correct value.
 */
class ShaderVariable
{

public:

     /** \brief identificator
     */
    std::string name;

     /** \brief OpenGL handler for uniform variable
     */
    GLint location;
    /** \brief constructor
    * \param handler is shader program OpenGL handler
    * \param aname is string containing uniform variable name
    */
    ShaderVariable(GLhandleARB handler, std::string aname)
    {
        name = aname;
        const char* cname = name.c_str();
        location = glGetUniformLocationARB(handler, cname );
#ifdef __DEBUG_SHADERS__
        if (location == -1)
        {
            std::string buffer = "Not found variable ";
            buffer.append(cname);
            printf("%s\n",buffer.c_str());
        }
#endif
    }
    /** \brief passing the Matrix4GL class to the shader. You have to bind shader before using this.
     * \param mat is matrix to pass
     */
    void pass_matrix(Matrix4GL &mat)
    {
        glUniformMatrix4fvARB(location,1,GL_FALSE,(float*)mat.get_matrix());
    }
    /** \brief passing the Matrix4GL class to the shader. You have to bind shader before using this.
     * \param mat is matrix to pass
     * \param idx texture stage index. From GL_TEXTURE0 to GL_TEXTURE32 in case of OpenGL 2.0
     */
    void pass_sampler(Texture &tex, int idx)
    {
        glActiveTexture(GL_TEXTURE0+idx);
        glEnable(GL_TEXTURE);
        glBindTexture(GL_TEXTURE_2D, tex.handler);
        glUniform1i(location,idx);
    }

    void pass_float(float val)
    {
         glUniform1f(location,val);
    }

    void pass_float3(float val1,float val2,float val3)
    {
        glUniform3f(location,val1,val2,val3);
    }

    void pass_float4(float val1,float val2,float val3,float val4)
    {
        glUniform4f(location,val1,val2,val3,val4);
    }

};

 /** \brief wrapper class for shader. By now supports pixel and vertex shaders only.
 */
class Shader
{
public:
    /** \brief dictionary of variables. Use add_varibale to fill it.
    */
	hash_map<std::string,boost::shared_ptr<ShaderVariable> >variables;
	/** \brief shader program handler.
    */
    GLuint shader_program;
    /** \brief vertex program handler. Used only before linking.
    */
    GLuint shader_vp;

    /** \brief fragment program handler. Used only before linking.
    */
    GLuint shader_fp;
    /** \brief geometry program handler. Used only before linking.
    */
    GLuint shader_gp;
    /** \brief filling the dictionary with named variables
    *   \param name identificator
    */
    void add_variable(std::string name)
    {
        if(variables.count(name)) throw "alread exists";
		variables[name] = boost::shared_ptr<ShaderVariable>(new ShaderVariable(this->shader_program,name));
    }
    /** \brief accessing the shader variable by name
    *   \param name identificator
    */
    ShaderVariable& operator [](std::string name)
    {
        return *(variables[name].get());
    }

    /** \brief loads,compiles and stores the compilation log into shader.log
    *   \param name_vp vertex shader filename
    *   \param name_fp fragment shader filename
    */
    void load(std::string name_vp,std::string name_fp)
    {

        char ebuffer[2048];
		std::ofstream log("shader.log",std::ofstream::app);

        shader_program = glCreateProgramObjectARB();
        if (name_vp.length()>0)
        {
            std::string source;
            std::string buffer;
            std::ifstream stream(name_vp.c_str() , std::ifstream::in);
            if(stream.fail())
            {
                std::cout << "Couldn't open file "<<name_vp<<std::endl;
                return;
            }

            while (!stream.eof())
            {
                getline(stream,buffer);
                source.append(buffer);
                source.append("\n");
            }
            stream.close();
            const char* src = source.c_str();
            shader_vp = glCreateShaderObjectARB(GL_VERTEX_SHADER_ARB);
            glShaderSourceARB(shader_vp, 1, &src, NULL);
            glCompileShaderARB(shader_vp);
            glGetInfoLogARB(shader_vp, 2048, 0,ebuffer);
			log << "FILE " << name_vp << std::endl;
			log << ebuffer << std::endl;
			glAttachObjectARB(shader_program, shader_vp);
        }

        if (name_fp.length()>0)
        {
            std::string source;
            source.clear();
            std::string buffer;
            std::ifstream stream(name_fp.c_str() , std::ifstream::in );
            if(stream.fail())
            {
                std::cout << "Couldn't open file "<<name_fp<<std::endl;
                return;
            }
            while (!stream.eof()&&!stream.bad())
            {
                getline(stream,buffer);
                source.append(buffer);
                source.append("\n");
            }
            stream.close();
            const char* src = source.c_str();
            shader_fp = glCreateShaderObjectARB(GL_FRAGMENT_SHADER_ARB);
            glShaderSourceARB(shader_fp, 1, &src, NULL);
            glCompileShaderARB(shader_fp);

            glGetInfoLogARB(shader_fp, 2048, 0,ebuffer);
            log << "FILE " << name_vp << std::endl;
			log << ebuffer << std::endl;
            glAttachObjectARB(shader_program, shader_fp);
        }

        glLinkProgramARB(shader_program);
        glGetInfoLogARB(shader_program, 2048, 0,ebuffer);
		log << "LINKING"<< std::endl;
		log << ebuffer << std::endl;
        log.close();
    }
    /** \brief simple destructor. We need to handle vertex and fragment shader manually. Also we need to release the shader program.
    */
    virtual ~Shader()
    {
        glDeleteObjectARB(shader_program);
        glDeleteObjectARB(shader_vp);
        glDeleteObjectARB(shader_fp);
    }
    /** \brief activate shader
    */
    void bind()
    {
        glUseProgramObjectARB(shader_program);
    }

    /** \brief deactivate shader
    */
    void unbind()
    {
        glUseProgramObjectARB(0);
    }
};

/** \brief not stable interface.
    */
class ShaderAttribute
{
public:
    std::string name;
    GLint location;
    ShaderAttribute(const Shader& shader,std::string aname)
    {
        name = aname;
        const char* cname = name.c_str();
        location = glGetAttribLocationARB(shader.shader_program, cname );
    }
    void pass_vector3(void* array,unsigned int stride)
    {
        glEnableVertexAttribArrayARB(location);
        glVertexAttribPointerARB(location,3,GL_FLOAT,0,stride,array);
    }
};
/** \brief FBO based rendertarget for off-screen rendering
    */
class RenderTarget
{
private:
    /** \brief viewport width
    */
    GLuint viewportx;
    /** \brief viewport height
    */
    GLuint viewporty;
    /** \brief FBO handler
    */
    GLuint handler;
    /** \brief FBO renderbuffer handler
    */
    GLuint render;
public:

    /** \brief simple constructor. Constructor doesn't initialize the FBO.
    */
    RenderTarget() {}

    /** \brief destructor
    */
    ~RenderTarget()
    {
        if(handler)
            glDeleteFramebuffersEXT(1,&handler);
        if(render)
            glDeleteRenderbuffers(1,&render);
    }
    /** \brief FBO initialization
    *   \param width
    *   \param height
    *   \param color texture. RGB or RGBA format.
    *   \param depth textur GL_DEPTH_COMPONENT24
    */
    void init(int sizex,int sizey, const Texture& color,const Texture& depth)
    {
        viewportx = sizex;
        viewporty = sizey;
        glGenFramebuffersEXT(1, &handler);
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handler);
        glGenRenderbuffersEXT(1, &render);
        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, render);
        glRenderbufferStorageEXT(GL_RENDERBUFFER_EXT, GL_DEPTH_COMPONENT24, sizex, sizey);
        glBindRenderbufferEXT(GL_RENDERBUFFER_EXT, 0);
        if(color.handler)
            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_COLOR_ATTACHMENT0_EXT, GL_TEXTURE_2D, color.handler, 0);
        if (depth.handler)
        {
            glFramebufferRenderbufferEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_RENDERBUFFER_EXT, render);
            glFramebufferTexture2DEXT(GL_FRAMEBUFFER_EXT, GL_DEPTH_ATTACHMENT_EXT, GL_TEXTURE_2D, depth.handler, 0);
        }
        GLenum status = glCheckFramebufferStatusEXT(GL_FRAMEBUFFER_EXT);
        if (status != GL_FRAMEBUFFER_COMPLETE_EXT) printf("buffer creation failed\n");
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
    }

    /** \brief FBO bind function. The viewport bit is being pushed into the stack
    */
    void bind()
    {
        glPushAttrib(GL_VIEWPORT_BIT);
        glViewport(0,0,viewportx,viewporty);
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, handler);
    }

    /** \brief FBO bind function. The viewport bit is being popped the stack
    */
    void unbind()
    {
        glBindFramebufferEXT(GL_FRAMEBUFFER_EXT, 0);
        glPopAttrib();
    }

};

struct Effect
{
    Shader shader;

};

struct Material
{
    Effect effect;

};

/** \brief common data needed for render pass. It's better to keep passes as much atomized as possible.
*/
struct Pass
{
    /** \brief matrices accessible by name
    */
	hash_map<std::string,TestbedGL::Matrix4GL>matrices;
    /** \brief textures accessible by name
    */
	hash_map<std::string,TestbedGL::Texture>textures;
    /** \brief vector of nodes
    */
	std::vector<boost::shared_ptr<TestbedGL::SceneNode> >nodes;
	/** \brief Render target
	*/
	TestbedGL::RenderTarget rt;

    /** \brief shader.
    */
	TestbedGL::Shader shader;
};
/** \brief window initialization class
*/
class Window
{
private:
public:
    /** \brief constructor with SDL window initialization
    *   \param width
    *   \param height
    *   \param color bpp color 16,24,32
    *   \param fullscreen
    */
    Window(int width,int height,int bpp,bool fullscreen=false)
    {
        const SDL_VideoInfo* info = NULL;
        int flags = 0;
        printf("Startup...\n");
        if ( SDL_Init( SDL_INIT_VIDEO ) < 0 )
        {
            fprintf( stderr, "Video initialization failed: %s\n",SDL_GetError( ) );
            fflush(stderr);
            exit( 1 );
        }
        printf("SDL initialization...\n");
        info = SDL_GetVideoInfo( );
        if ( !info )
        {
            fprintf( stderr, "Video query failed: %s\n",SDL_GetError( ) );
            fflush(stderr);
            exit( 1 );
        }

        printf("Quering video...\n");

        SDL_WarpMouse(width/2,height/2);
        SDL_GL_SetAttribute( SDL_GL_RED_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_GREEN_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_BLUE_SIZE, 5 );
        SDL_GL_SetAttribute( SDL_GL_DEPTH_SIZE, bpp );
        SDL_GL_SetAttribute( SDL_GL_DOUBLEBUFFER, 1 );
        flags = SDL_OPENGL | (SDL_FULLSCREEN*fullscreen);
        if ( SDL_SetVideoMode( width, height, bpp, flags ) == 0 )
        {
            fprintf( stderr, "Video mode set failed: %s\n",SDL_GetError( ) );
            exit( 1 );
        }
        printf("Video initialization...\n");
        SDL_EnableUNICODE(1);
        SDL_WM_GrabInput(SDL_GRAB_ON);
        SDL_ShowCursor(0);
        SDL_EnableKeyRepeat(1,1);
        GLenum err = glewInit();
        printf("OpenGL initialization...\n");
        if (GLEW_OK != err)
        {
            fprintf(stderr, "Error: %s\n", glewGetErrorString(err));
        }
    }
};
};
#endif
